<!DOCTYPE html>
<head>
  <title>Vue双向数据绑定</title>
</head>
<style>
</style>
<body>
  <div id="app">
    <input type="text" v-model="text">
    {{ text }}
  </div>
</body>

<script>
    function Vue (option){
        this.data = option.data
        let id = option.el;
        let dom = nodeFragment(document.getElementById(id),this)
        observer(this.data,this)
        document.getElementById(id).appendChild(dom);
    }   
    // 实例化一个观察者类
    class Watcher{
        constructor (vm,node,name) {
           Dep.target = this
           this.vm = vm;
           this.node = node;
           this.name = name;
           this.update()
        }
        get () {
            this.value = this.vm.data[this.name];
        }
        update() {
            this.get()
            this.node.nodeValue = this.value
        }
    }
    class Dep {
        constructor () {
            this.subs = [];
        }
        addSub(sub) {
            this.subs.push(sub)
        }
        notify() {
            this.subs.forEach((item) => {
                item.update();
            })
        }
    }
    // 使用虚拟dom 减少dom开销
    function nodeFragment (node,vm) {
        var fragment = document.createDocumentFragment();
        var child;
        while(child = node.firstChild){
            compile(child,vm);
            fragment.appendChild(child);
        }
        return fragment;
    }
    // 实例化Vue
    var vm = new Vue({
        el:'app',
        data:{
            text:'段哥哥'
        }
    })
    // 解析指令 v-model
    function compile (node,vm) {
        var reg = /\{\{(.*)\}\}/;
        // 如果是元素节点
        if(node.nodeType == 1){
            var attr = node.attributes;
            for(let i=0;i<attr.length;i++){
                if(attr[i].nodeName== 'v-model'){
                    name = attr[i].nodeValue;
                    node.addEventListener('input',(e) => {
                        vm.data[name] = e.target.value
                    })
                    node.value = vm.data[name]
                    node.removeAttribute('v-model');
                }
            }
        }
        // 如果是文本节点
        if(node.nodeType == 3){
            if(reg.test(node.nodeValue)){
                var name = RegExp.$1;
                new Watcher(vm,node,name.trim());
            }
        }
    }
    function defineReactive (obj,key,val){
        var dep = new Dep ();
        Object.defineProperty(obj,key,{
            get:() => {
              return val;
            },
            set:(newVal) => {
                if(val == newVal){
                    return;
                }
                if(Dep.target){
                  dep.addSub(Dep.target)
                }
                val = newVal;
                dep.notify()
                Dep.target = null;
            }
        })
    }
    // 观察者
    function observer (obj,vm){
        for(let key of Object.keys(obj)){
            defineReactive(vm['data'],key,obj[key])
        }
    }

</script>